﻿using Sage.WindowsHotKey.Exceptions;

namespace Sage.WindowsHotKey
{

    /// <summary>
    /// 管理键盘快捷键的显示和注册
    /// </summary>
    /// <param name="hotKeyManager">热键管理器，用于热键注册时的交互</param>
    /// <param name="continueText">按下等待显示的文本</param>
    public sealed class KeyDisplayManager(HotKeyManager hotKeyManager, string? continueText = null)
    {
        /// <summary>
        /// 热键管理类
        /// </summary>
        private readonly HotKeyManager _hotKeyManager = hotKeyManager ?? throw new ArgumentNullException(nameof(hotKeyManager));
        /// <summary>
        /// 组合键提示文本
        /// </summary>
        private string? _continueText = continueText;
        /// <summary>
        /// 存储当前快捷键状态
        /// </summary>
        private ShortcutState _currentState = new();

        /// <summary>
        /// 忽略按键的存储
        /// </summary>
        private readonly HashSet<Keys> _ignoredKeys = [];
        /// <summary>
        /// 忽略组合按键的存储列表
        /// </summary>
        private readonly HashSet<Keys> _ignoredKeyCombinations = [];
        /// <summary>
        /// 获取或设置是否允许注册单个按键（不包含修饰键的按键）
        /// </summary>
        public bool AllowSingleKey { get; set; }
        /// <summary>
        /// 当有效的快捷键组合被注册时触发
        /// </summary>
        public event EventHandler<ShortcutRegisteredEventArgs>? ShortcutRegistered;

        /// <summary>
        /// 获取或设置等待按键时的提示文本，为null时只显示加号
        /// </summary>
        public string? ContinueText
        {
            get => _continueText;
            set => _continueText = string.IsNullOrWhiteSpace(value) ? null : value;
        }
        /// <summary>
        /// 获取所有已设置的忽略按键
        /// </summary>
        public HashSet<Keys> GetIgnoredKey => _ignoredKeys;

        /// <summary>
        /// 获取所有已设置的忽略组合按键
        /// </summary>
        public IReadOnlyCollection<Keys> IgnoredKeyCombinations => _ignoredKeyCombinations;

        /// <summary>
        /// 添加需要忽略的单个按键
        /// </summary>
        /// <param name="key">要忽略的按键</param>
        public void AddIgnoredKey(Keys key) => _ignoredKeys.Add(key);

        /// <summary>
        /// 移除忽略的单个按键
        /// </summary>
        /// <param name="key">要移除的按键</param>
        public void RemoveIgnoredKey(Keys key) => _ignoredKeys.Remove(key);

        /// <summary>
        /// 清除所有忽略的按键设置
        /// </summary>
        public void ClearIgnoredKeys()
        {
            _ignoredKeys.Clear();
            _ignoredKeyCombinations.Clear();
        }
        /// <summary>
        /// 移除忽略的组合键
        /// </summary>
        /// <param name="keyCombination">要移除的组合键</param>
        public void RemoveIgnoredKeyCombination(Keys keyCombination) => _ignoredKeyCombinations.Remove(keyCombination);

        /// <summary>
        /// 添加需要忽略的组合键
        /// </summary>
        /// <param name="keyCombination">要忽略的组合键</param>
        public void AddIgnoredKeyCombination(Keys keyCombination) => _ignoredKeyCombinations.Add(keyCombination);

        /// <summary>
        /// 处理按键按下事件
        /// </summary>
        /// <param name="e">按键事件参数</param>
        /// <param name="hotKeyName">热键名称</param>
        /// <returns>当前快捷键的显示文本</returns>
        public string ProcessKeyDown(KeyEventArgs e, string hotKeyName)
        {

            var keyData = GetKeyData(e);

            // 如果是修饰键
            if (IsModifierKey(e.KeyCode))
            {
                UpdateState(keyData, hotKeyName);
                return _currentState.KeyData.ToKeyString(_continueText);
            }

            // 检查是否是被忽略的按键
            if (IsRegularKey(e.KeyCode))
            {
                if (_ignoredKeys.Contains(e.KeyCode))
                {
                    // 获取已注册的热键
                    var registeredHotKey = _hotKeyManager.GetHotKeyByName(hotKeyName);
                    if (registeredHotKey != null)
                    {
                        UpdateState(registeredHotKey.KeyData, registeredHotKey.Name);
                        return registeredHotKey.KeyData.ToKeyString();
                    }
                    return string.Empty;
                }

                UpdateState(keyData, hotKeyName);

                // 检查是否是被忽略的组合键
                if (_ignoredKeyCombinations.Contains(keyData))
                {
                    var registeredHotKey = _hotKeyManager.GetHotKeyByName(hotKeyName);
                    if (registeredHotKey != null)
                    {
                        UpdateState(registeredHotKey.KeyData, registeredHotKey.Name);
                        return registeredHotKey.KeyData.ToKeyString();
                    }
                    return keyData.ToKeyString();
                }

                // 检查是否应该触发注册事件
                if (ShouldTriggerRegistration())
                {
                    var eventArgs = new ShortcutRegisteredEventArgs(
                        _currentState.KeyData,
                        _currentState.KeyData.ToKeyString(),
                        _currentState.HotKeyName);

                    OnShortcutRegistered(eventArgs);

                    // 如果处理结果为false，恢复到已注册的热键
                    if (!eventArgs.Handled)
                    {
                        var registeredHotKey = _hotKeyManager.GetHotKeyByName(hotKeyName);
                        if (registeredHotKey != null)
                        {
                            UpdateState(registeredHotKey.KeyData, registeredHotKey.Name);
                            return registeredHotKey.KeyData.ToKeyString();
                        }
                        else
                        {
                            ClearState();
                            return string.Empty;
                        }
                    }
                }
            }

            e.SuppressKeyPress = true;
            e.Handled = true;

            return _currentState.KeyData.ToKeyString(_continueText);
        }

        /// <summary>
        /// 处理按键释放事件
        /// </summary>
        /// <param name="e">按键事件参数</param>
        /// <returns>当前快捷键的显示文本，如果清除则返回空字符串</returns>
        public string ProcessKeyUp(KeyEventArgs e)
        {
            // 如果当前按键是被忽略的键
            if (IsRegularKey(e.KeyCode) && _ignoredKeys.Contains(e.KeyCode))
            {
                var registeredHotKey = _hotKeyManager.GetHotKeyByName(_currentState.HotKeyName);
                if (registeredHotKey != null)
                {
                    UpdateState(registeredHotKey.KeyData, registeredHotKey.Name);
                    return registeredHotKey.KeyData.ToKeyString();
                }
                return string.Empty;
            }

            // 如果只有修饰键被按下（没有其他键），并且当前释放的是修饰键
            if (_currentState.HasModifiers && !_currentState.HasNonModifier &&
                IsModifierKey(e.KeyCode))
            {
                var registeredHotKey = _hotKeyManager.GetHotKeyByName(_currentState.HotKeyName);
                if (registeredHotKey != null)
                {
                    UpdateState(registeredHotKey.KeyData, registeredHotKey.Name);
                    return registeredHotKey.KeyData.ToKeyString();
                }
                else
                {
                    ClearState();
                    return string.Empty;
                }
            }

            return _currentState.KeyData.ToKeyString(_continueText);
        }

        /// <summary>
        /// 检查是否应该触发注册事件
        /// </summary>
        /// <returns>如果应该触发注册事件则返回true，否则返回false</returns>
        private bool ShouldTriggerRegistration() => AllowSingleKey && _currentState.HasNonModifier ||
            _currentState.HasModifiers && _currentState.HasNonModifier;

        /// <summary>
        /// 获取当前设置的快捷键
        /// </summary>
        /// <returns>包含按键值和显示文本的元组</returns>
        public (Keys keyData, string displayText) GetCurrentShortcut() =>
                   (_currentState.KeyData, _currentState.KeyData.ToKeyString(_continueText));

        /// <summary>
        /// 触发快捷键注册事件
        /// </summary>
        /// <param name="args">快捷键注册事件参数</param>
        private void OnShortcutRegistered(ShortcutRegisteredEventArgs args)
        {
            ShortcutRegistered?.Invoke(this, args);
        }

        /// <summary>
        /// 从KeyEventArgs获取完整的按键数据，特殊处理修饰键
        /// </summary>
        /// <param name="e">按键事件参数</param>
        /// <returns>组合后的按键值</returns>
        private static Keys GetKeyData(KeyEventArgs e)
        {
            var modifiers = Keys.None;

            // 检查所有修饰键的状态
            if (e.Control || (Control.ModifierKeys & Keys.Control) == Keys.Control)
                modifiers |= Keys.Control;
            if (e.Shift || (Control.ModifierKeys & Keys.Shift) == Keys.Shift)
                modifiers |= Keys.Shift;
            if (e.Alt || (Control.ModifierKeys & Keys.Alt) == Keys.Alt)
                modifiers |= Keys.Alt;

            var keyData = e.KeyCode;

            // 如果按下的是修饰键本身
            if (keyData is Keys.ControlKey or Keys.ShiftKey or Keys.Menu)
            {
                // 返回所有当前按下的修饰键组合
                return modifiers != Keys.None ? modifiers : Keys.None;
            }

            // 对于非修饰键，组合所有修饰键状态
            return keyData | modifiers;
        }

        /// <summary>
        /// 更新当前快捷键状态
        /// </summary>
        /// <param name="keyData">组合的按键值</param>
        /// <param name="hotKeyName">热键名称</param>
        private void UpdateState(Keys keyData, string hotKeyName)
        {

            var modifiers = keyData & Keys.Modifiers;
            var mainKey = keyData & Keys.KeyCode;

            _currentState = _currentState with
            {
                HasModifiers = modifiers != Keys.None,
                HasNonModifier = mainKey != Keys.None &&
                                mainKey != Keys.ControlKey &&
                                mainKey != Keys.ShiftKey &&
                                mainKey != Keys.Menu,
                KeyData = keyData,
                HotKeyName = hotKeyName
            };
        }

        /// <summary>
        /// 清除当前状态
        /// </summary>
        private void ClearState()
        {
            _currentState = new ShortcutState();
        }
        //private void ClearState() => _currentState = new ShortcutState();

        /// <summary>
        /// 判断是否为修饰键
        /// </summary>
        /// <param name="key">要判断的按键</param>
        /// <returns>是否为修饰键</returns>
        private static bool IsModifierKey(Keys key) => key is Keys.ControlKey or Keys.ShiftKey or Keys.Menu;

        /// <summary>
        /// 判断是否为普通键（非修饰键）
        /// </summary>
        /// <param name="key">要判断的按键</param>
        /// <returns>是否为普通键</returns>
        private static bool IsRegularKey(Keys key) => !IsModifierKey(key);
    }

    /// <summary>
    /// 快捷键注册成功事件的参数类
    /// </summary>
    /// <param name="keyData">按键值</param>
    /// <param name="displayText">显示文本</param>
    /// <param name="hotKeyName">热键名称</param>
    public class ShortcutRegisteredEventArgs(Keys keyData, string displayText, string hotKeyName) : EventArgs
    {

        /// <summary>
        /// 获取按键值
        /// </summary>
        public Keys KeyData { get; } = keyData;

        /// <summary>
        /// 获取显示文本
        /// </summary>
        public string DisplayText { get; } = displayText;

        /// <summary>
        /// 热键名称
        /// </summary>
        public string HotKeyName { get; } = hotKeyName;

        /// <summary>
        /// 指示快捷键是否有效并应该被显示
        /// </summary>
        public bool Handled { get; set; } = true;
    }

    /// <summary>
    /// 表示快捷键状态的记录类型
    /// </summary>
    public record ShortcutState
    {
        /// <summary>
        /// 快捷键名称
        /// </summary>
        public string HotKeyName { get; init; } = string.Empty;
        /// <summary>
        /// 是否包含修饰键（Ctrl、Shift、Alt）
        /// </summary>
        public bool HasModifiers { get; init; }

        /// <summary>
        /// 是否包含非修饰键
        /// </summary>
        public bool HasNonModifier { get; init; }

        /// <summary>
        /// 组合后的按键值
        /// </summary>
        public Keys KeyData { get; init; } = Keys.None;
        /// <summary>
        /// 指示当前快捷键状态是否有效
        /// </summary>
        public bool IsValid { get; init; } = true;
    }

    /// <summary>
    /// KeyDisplayManager的扩展方法
    /// </summary>
    public static class KeyDisplayManagerExtensions
    {
        /// <summary>
        /// 批量添加要忽略的按键
        /// </summary>
        /// <param name="manager">键盘显示管理器实例</param>
        /// <param name="keys">要忽略的按键数组</param>
        /// <returns>管理器实例，支持链式调用</returns>
        public static KeyDisplayManager IgnoreKeys(this KeyDisplayManager manager, params Keys[] keys)
        {
            foreach (var key in keys)
            {
                manager.AddIgnoredKey(key);
            }
            return manager;
        }

        /// <summary>
        /// 批量添加要忽略的组合键
        /// </summary>
        /// <param name="manager">键盘显示管理器实例</param>
        /// <param name="combinations">要忽略的组合键数组</param>
        /// <returns>管理器实例，支持链式调用</returns>
        public static KeyDisplayManager IgnoreCombinations(this KeyDisplayManager manager, params Keys[] combinations)
        {
            foreach (var combination in combinations)
            {
                manager.AddIgnoredKeyCombination(combination);
            }
            return manager;
        }
    }
}
